package com.hpq.chat.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import com.hpq.chat.dao.*;
import com.hpq.chat.domain.dto.MsgReadInfoDTO;
import com.hpq.chat.domain.enums.MessageMarkActTypeEnum;
import com.hpq.chat.domain.enums.MessageTypeEnum;
import com.hpq.chat.domain.model.*;
import com.hpq.chat.domain.vo.request.*;
import com.hpq.chat.domain.vo.request.member.MemberReq;
import com.hpq.chat.domain.vo.request.msg.ChatMessageBaseReq;
import com.hpq.chat.domain.vo.request.msg.ChatMessageMarkReq;
import com.hpq.chat.domain.vo.response.msg.ChatMessageReadResp;
import com.hpq.chat.domain.vo.response.msg.ChatMessageResp;
import com.hpq.chat.service.ChatService;
import com.hpq.chat.service.ContactService;
import com.hpq.chat.service.adapter.MemberAdapter;
import com.hpq.chat.service.adapter.MessageAdapter;
import com.hpq.chat.service.adapter.RoomAdapter;
import com.hpq.chat.service.cache.RoomCache;
import com.hpq.chat.service.cache.RoomGroupCache;
import com.hpq.chat.service.helper.ChatMemberHelper;
import com.hpq.chat.service.srategy.mark.AbstractMsgMarkStrategy;
import com.hpq.chat.service.srategy.mark.MsgMarkFactory;
import com.hpq.chat.service.srategy.msg.AbstractMsgHandler;
import com.hpq.chat.service.srategy.msg.MsgHandlerFactory;
import com.hpq.chat.service.srategy.msg.RecallMsgHandler;
import com.hpq.common.annotation.RedissonLock;
import com.hpq.common.domain.enums.NormalOrNoEnum;
import com.hpq.common.domain.vo.request.CursorPageBaseReq;
import com.hpq.common.domain.vo.response.CursorPageBaseResp;
import com.hpq.common.event.MessageSendEvent;
import com.hpq.common.utils.AssertUtil;
import com.hpq.user.dao.UserDAO;
import com.hpq.user.domain.enums.ChatActiveStatusEnum;
import com.hpq.user.domain.enums.RoleEnum;
import com.hpq.user.domain.model.User;
import com.hpq.user.service.RoleService;
import com.hpq.websocket.domain.vo.response.ChatMemberResp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;


@Service
public class ChatServiceImpl implements ChatService {
    @Autowired
    private RoomCache roomCache;
    @Autowired
    private RoomFriendDAO roomFriendDao;
    @Autowired
    private RoomGroupCache roomGroupCache;
    @Autowired
    private GroupMemberDAO groupMemberDao;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @Autowired
    private MessageMarkDAO messageMarkDao;
    @Autowired
    private MessageDAO messageDao;
    @Autowired
    private ContactDAO contactDao;
    @Autowired
    private RecallMsgHandler recallMsgHandler;
    @Autowired
    private RoleService roleService;
    @Autowired
    private ContactService contactService;
    @Autowired
    private UserDAO userDao;
    @Autowired
    private RoomGroupDAO roomGroupDao;

    @Override
    public Long sendMsg(ChatMessageReq request, Long uid) {
        //检查发送消息合法性
        check(request, uid);
        //根据消息类型获取对应的策略处理器
        AbstractMsgHandler<?> msg = MsgHandlerFactory.getStrategyNoNull(request.getMsgType());
        //保存消息并返回消息ID
        Long msgId = msg.checkAndSaveMsg(request, uid);
        //发布消息发送事件
        applicationEventPublisher.publishEvent(new MessageSendEvent(this,msgId));
        return msgId;

    }

    @Override
    public ChatMessageResp getMsgResp(Message message, Long receiveUid) {
        return CollUtil.getFirst(getMsgRespBatch(Collections.singletonList(message), receiveUid));
    }
    @Override
    public ChatMessageResp getMsgResp(Long msgId, Long receiveUid) {
        Message msg = messageDao.getById(msgId);
        return getMsgResp(msg, receiveUid);
    }

    @Override
    public CursorPageBaseResp<ChatMessageResp> getMsgPage(ChatMessagePageReq req, Long receiveUid) {
        Long lastMsgId = getLastMsgId(req.getRoomId(), receiveUid);
        CursorPageBaseResp<Message> cursorPage = messageDao.getCursorPage(req.getRoomId(), req, lastMsgId);
        if(cursorPage.isEmpty()){
            return CursorPageBaseResp.empty();
        }
        return CursorPageBaseResp.init(cursorPage, getMsgRespBatch(cursorPage.getData(), receiveUid));
    }

    @Override
    public void recallMsg(Long uid, ChatMessageBaseReq req) {
        Message message = messageDao.getById(req.getMsgId());
        //判断是否撤回
        checkRecall(uid, message);
        recallMsgHandler.recall(uid, message);
    }

    @Override
    @RedissonLock(key = "#uid")
    public void setMsgMark(Long uid, ChatMessageMarkReq request) {
        AbstractMsgMarkStrategy strategy = MsgMarkFactory.getStrategyNoNull(request.getMarkType());
        switch (MessageMarkActTypeEnum.of(request.getActType())) {
            case MARK:
                strategy.mark(uid, request.getMsgId());
                break;
            case UN_MARK:
                strategy.unMark(uid, request.getMsgId());
                break;
        }
    }

    @Override
    public CursorPageBaseResp<ChatMessageReadResp> getReadPage(ChatMessageReadReq req, Long uid) {
        Message message = messageDao.getById(req.getMsgId());
        AssertUtil.isEmpty(message, "消息不存在");
        AssertUtil.isNotEqual(uid,message.getFromUid(), "抱歉，您没有权限查看");
        CursorPageBaseResp<Contact> page;
        if(req.getSearchType() == 1){ //已读
            page = contactDao.getReadPage(message,req);
        }else{
            page = contactDao.getUnReadPage(message,req);
        }
        if(CollectionUtil.isEmpty(page.getData())){
            return CursorPageBaseResp.empty();
        }
        return CursorPageBaseResp.init(page, RoomAdapter.buildReadResp(page.getData()));
    }

    @Override
    public Collection<MsgReadInfoDTO> getMsgReadInfo(Long uid, ChatMessageReadInfoReq request) {
        List<Message> messages = messageDao.listByIds(request.getMsgIds());
        messages.forEach(message -> {
            AssertUtil.isNotEqual(uid, message.getFromUid(), "只能查询自己发送的消息");
        });
        return contactService.getMsgReadInfo(messages).values();

    }

    @Override
    @RedissonLock(key = "#uid")
    public void msgRead(Long uid, ChatMessageMemberReq request) {
        Contact contact = contactDao.get(uid, request.getRoomId());
        if (Objects.nonNull(contact)) {
            Contact update = new Contact();
            update.setId(contact.getId());
            update.setReadTime(new Date());
            contactDao.updateById(update);
        } else {
            Contact insert = new Contact();
            insert.setUid(uid);
            insert.setRoomId(request.getRoomId());
            insert.setReadTime(new Date());
            contactDao.save(insert);
        }
    }

    private void checkRecall(Long uid, Message message) {
        AssertUtil.isEmpty(message, "消息不存在");
        AssertUtil.isEqual(message.getType(), MessageTypeEnum.RECALL.getType(), "消息无法撤回");
        boolean hasPower = roleService.hasPower(uid, RoleEnum.CHAT_MANAGER);
        if(hasPower){
           return;
        }
        AssertUtil.isNotEqual(message.getFromUid(), uid, "抱歉，您没有权限撤回");
        AssertUtil.isTrue(message.getCreateTime().getTime() + 1000 * 60 * 2 > System.currentTimeMillis(), "消息已超过撤回时间");
    }

    private Long getLastMsgId( Long roomId, Long receiveUid) {
        Room room = roomCache.get(roomId);
        AssertUtil.isEmpty(room, "房间不存在");
        if(room.isHotRoom()){
            return null;
        }
        AssertUtil.isEmpty(receiveUid, "请先登录");
        Contact  contact = contactDao.getContact(receiveUid, roomId);
        return contact.getLastMsgId();
    }


    public List<ChatMessageResp> getMsgRespBatch(List<Message> messages, Long receiveUid) {
        if (CollectionUtil.isEmpty(messages)) {
            return new ArrayList<>();
        }
        //查询消息标志
        List<MessageMark> msgMark = messageMarkDao.getValidMarkByMsgIdBatch(messages.stream().map(Message::getId).collect(Collectors.toList()));
        return MessageAdapter.buildMsgResp(messages, msgMark, receiveUid);
    }

    private void check(ChatMessageReq request, Long uid) {
        Room room = roomCache.get(request.getRoomId());
        if(room.isHotRoom()){ //全员群跳过
            return;
        }
        if(room.isRoomFriend()){ //是否是好友房间
            RoomFriend roomFriend = roomFriendDao.getByRoomId(request.getRoomId());
            AssertUtil.isNotEqual(roomFriend.getStatus(), NormalOrNoEnum.NORMAL.getStatus(), "您已被对方拉黑");
            AssertUtil.isNotTrue(uid.equals(roomFriend.getUid1())|| uid.equals(roomFriend.getUid2()), "您已被对方拉黑");
        }
        if(room.isRoomGroup()){ //是否是群聊房间
            RoomGroup roomGroup = roomGroupCache.get(request.getRoomId());
            GroupMember member = groupMemberDao.getMember(roomGroup.getId(),uid);
            AssertUtil.isEmpty(member,"您已被踢出群聊");
        }
    }
    @Override
    public CursorPageBaseResp<ChatMemberResp> getMemberPage(List<Long> memberUidList, MemberReq request) {
        Pair<ChatActiveStatusEnum, String> pair = ChatMemberHelper.getCursorPair(request.getNextCursor());
        ChatActiveStatusEnum activeStatusEnum = pair.getKey();
        String timeCursor = pair.getValue();
        List<ChatMemberResp> resultList = new ArrayList<>();//最终列表
        Boolean isLast = Boolean.FALSE;
        if (activeStatusEnum == ChatActiveStatusEnum.ONLINE) {//在线列表
            CursorPageBaseResp<User> cursorPage = userDao.getCursorPage(memberUidList, new CursorPageBaseReq(request.getPageSize(), timeCursor), ChatActiveStatusEnum.ONLINE);
            resultList.addAll(MemberAdapter.buildMember(cursorPage.getData()));//添加在线列表
            if (cursorPage.getIsLast()) {//如果是最后一页,从离线列表再补点数据
                activeStatusEnum = ChatActiveStatusEnum.OFFLINE;
                Integer leftSize = request.getPageSize() - cursorPage.getData().size();
                cursorPage = userDao.getCursorPage(memberUidList, new CursorPageBaseReq(leftSize, null), ChatActiveStatusEnum.OFFLINE);
                resultList.addAll(MemberAdapter.buildMember(cursorPage.getData()));//添加离线线列表
            }
            timeCursor = cursorPage.getNextCursor();
            isLast = cursorPage.getIsLast();
        } else if (activeStatusEnum == ChatActiveStatusEnum.OFFLINE) {//离线列表
            CursorPageBaseResp<User> cursorPage = userDao.getCursorPage(memberUidList, new CursorPageBaseReq(request.getPageSize(), timeCursor), ChatActiveStatusEnum.OFFLINE);
            resultList.addAll(MemberAdapter.buildMember(cursorPage.getData()));//添加离线线列表
            timeCursor = cursorPage.getNextCursor();
            isLast = cursorPage.getIsLast();
        }
        // 获取群成员角色ID
        List<Long> uidList = resultList.stream().map(ChatMemberResp::getUid).collect(Collectors.toList());
        RoomGroup roomGroup = roomGroupDao.getByRoomId(request.getRoomId());
        Map<Long, Integer> uidMapRole = groupMemberDao.getMemberMapRole(roomGroup.getId(), uidList);
        resultList.forEach(member -> member.setRoleId(uidMapRole.get(member.getUid())));
        //组装结果
        return new CursorPageBaseResp<>(ChatMemberHelper.generateCursor(activeStatusEnum, timeCursor), isLast, resultList);
    }


}
